AWS Lambda API を iOS アプリから呼び出す – AWS Lambda Advent Calendar 2014:5日目
Lambda を MBaaS として使う
先日米国ラスベガスで開催された『re:Invent 2014』にて発表されたAWS Lambdaに関するエントリを12/01から12/25まで毎日1本ずつ書いていくアドベントカレンダー『AWS Lambda Advent Calendar 2014』。このエントリは5日目の内容となります。
先日4日目のエントリはj3tm0t0さんの『AWS Lambda を CoffeeScript で書いてみた』でした。
本日5日目は、Lambda API を iOS アプリから直接呼び出すという内容です。
Lambda API を使うと、Lambda ファンクションをモバイルから直接呼び出すことができたり、Lambda ファンクションの取得や登録などといったことも可能です。Lambda ファンクションを呼び出すというのを始めに想像しますが、結構試されている記事も多いので、今回は Lambda ファンクションの情報を取得し、iOS アプリに表示するという処理を実装してみたいと思います。
AWS Mobile SDK は未対応
Lambda API の呼び出しは AWS Mobile SDK を使えば楽勝!と言いたいところですが、現在(2014/12/05)の時点では Lambda は未対応です。
それでもどうしても iOS から呼んでみたい!ということで、今回は Lambda のクライアントのクラスを自作しました。実装は少し長いので、Lambda のクラス実装部分だけ載せます。
#import "AWSNetworking.h" #import "AWSModel.h" @class AWSLambdaGetFunctionRequest; @class AWSLambdaGetFunctionResponse; @class AWSLambdaFunctionConfiguration; @class AWSLambdaFunctionCodeLocation; @interface AWSLambdaGetFunctionRequest : AWSRequest @property (nonatomic, strong) NSString *functionName; @end @interface AWSLambdaGetFunctionResponse : AWSModel @property (nonatomic, strong) AWSLambdaFunctionConfiguration *configuration; @property (nonatomic, strong) AWSLambdaFunctionCodeLocation *code; @end @interface AWSLambdaFunctionConfiguration : AWSModel @property (nonatomic, strong) NSString *functionName; @property (nonatomic, strong) NSString *functionARN; @property (nonatomic, strong) NSString *configurationId; @property (nonatomic, strong) NSString *runtime; @property (nonatomic, strong) NSString *role; @property (nonatomic, strong) NSString *handler; @property (nonatomic, strong) NSString *mode; @property (nonatomic, strong) NSString *codeSize; @property (nonatomic, strong) NSString *functionDescription; @property (nonatomic, strong) NSString *timeout; @property (nonatomic, strong) NSString *memorySize; @property (nonatomic, strong) NSString *lastModified; @end @interface AWSLambdaFunctionCodeLocation : AWSModel @property (nonatomic, strong) NSString *location; @property (nonatomic, strong) NSString *repositoryType; @end
#import "AWSLambdaModel.h" @implementation AWSLambdaGetFunctionRequest + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{@"functionName" : @"FunctionName"}; } @end @implementation AWSLambdaGetFunctionResponse : AWSModel + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{@"configuration" : @"Configuration", @"code" : @"Code"}; } + (NSValueTransformer *)configurationJSONTransformer { return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:[AWSLambdaFunctionConfiguration class]]; } + (NSValueTransformer *)codeJSONTransformer { return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:[AWSLambdaFunctionCodeLocation class]]; } @end @implementation AWSLambdaFunctionConfiguration : AWSModel + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{@"functionName" : @"FunctionName", @"functionARN" : @"FunctionARN", @"configurationId" : @"ConfigurationId", @"runtime" : @"Runtime", @"role" : @"Role", @"handler" : @"Handler", @"mode" : @"Mode", @"codeSize" : @"CodeSize", @"functionDescription" : @"Description", @"timeout" : @"Timeout", @"memorySize" : @"MemorySize", @"lastModified" : @"LastModified"}; } @end @implementation AWSLambdaFunctionCodeLocation : AWSModel + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{@"repositoryType" : @"RepositoryType", @"location" : @"Location"}; } @end
#import "AWSService.h" #import "AWSLambdaModel.h" @class BFTask; @interface AWSLambda : AWSService @property (nonatomic, strong, readonly) AWSServiceConfiguration *configuration; + (instancetype)defaultLambda; - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration; // Lambdaファンクションの情報の取得 - (BFTask *)getFunction:(AWSLambdaGetFunctionRequest *)request; @end
@implementation AWSLambda // ...省略... #pragma mark - Service method - (BFTask *)getFunction:(AWSLambdaGetFunctionRequest *)request { return [self invokeRequest:request HTTPMethod:AWSHTTPMethodGET URLString:@"/2014-11-13/functions/{FunctionName}/" targetPrefix:@"" operationName:@"GetFunction" outputClass:nil]; } @end
この他に、次のような修正を行います。
- lambda-2014-11-11.json の追加
- AWSServiceType に AWSServiceLambda を追加
- AWSEndpoint#initWithRegion:service:useUnsafeURL: に分岐処理を追加
Lambda ファンクションの実装
今回はとりあえず取得できるか試したかったので、 Lambda ファンクションはかなり簡単なものです。
console.log('Loading event'); exports.handler = function(event, context) { context.done(null, 'Hello, iOS Client!'); };
iOS アプリ側の実装
もろもろ用意ができたところで、最後に呼び出すところの実装です。もちろん Cognito で認証します!
- (void)getFunction { // Cognito認証 AWSCognitoCredentialsProvider *provider = [AWSCognitoCredentialsProvider credentialsWithRegionType:AWSRegionUSEast1 accountId:@"xxxxxxxxxxxx" identityPoolId:@"us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" unauthRoleArn:@"arn:aws:iam::xxxxxxxxxxxx:role/Cognito_SampleAppUnauth_DefaultRole" authRoleArn:@"arn:aws:iam::xxxxxxxxxxxx:role/Cognito_SampleAppAuth_DefaultRole" logins:nil]; // Lambdaファンクションを取得する AWSLambdaGetFunctionRequest *request = [AWSLambdaGetFunctionRequest new]; request.functionName = @"helloMobileClient"; AWSLambda *lambda = [AWSLambda defaultLambda]; [[lambda getFunction:request] continueWithBlock:^id(BFTask *task) { if (task.error) { NSLog(@"エラー : %@", task.error); } else { NSLog(@"成功 : %@", task.result); AWSLambdaGetFunctionResponse *response = task.result; dispatch_async(dispatch_get_main_queue(), ^{ AWSLambdaFunctionConfiguration *configuration = response.configuration; NSString *result = [NSString stringWithFormat:@"configurationId = %@\nfunctionName = %@\nfunctionDescription = %@\nfunctionARN = %@\nhandler = %@\ncodeSize = %@\nmemorySize = %@\nmode = %@\nlastModified = %@\nrole = %@\nruntime = %@\ntimeout = %@", configuration.configurationId, configuration.functionName, configuration.functionDescription, configuration.functionARN, configuration.handler, configuration.codeSize, configuration.memorySize, configuration.mode, configuration.lastModified, configuration.role, configuration.runtime, configuration.timeout]; self.label.text = result; }); } return nil; }]; }
アプリを実行してログを見てみると、取得出来てるはずです!
まとめ
AWS Mobile SDK が対応していないけどどうしても試したい!っていう方はぜひ参考にしていただければと思います。クラスの設計自体は既存のサービスクラスと同じように実装しているので、今後もし AWS Mobile SDK 側がアップデートで Lambda を対応したとしても、利用するクラスの実装コードはきっとそのままで動くはずです!多分w
ちなみに AWS Mobile SDK の実装ソースをちょっと解読できたので、個人的にもタメになりました。
明日6日目はyoshidashingoさんによる「既存のアーキテクチャのどこかを置き換えてみる」です。お楽しみに!